package com.wtf.dsp.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.wtf.dsp.admin.core.model.GeneratorInfo;
import com.wtf.dsp.admin.core.provider.AbstractMybatisConfigProvider;
import com.wtf.dsp.admin.entity.WtfDspCodeTemplate;
import com.wtf.dsp.admin.entity.WtfDspDbConfig;
import com.wtf.dsp.admin.entity.WtfDspVm;
import com.wtf.dsp.admin.enums.CommonErrorEnum;
import com.wtf.dsp.admin.exception.WtfDspException;
import com.wtf.dsp.admin.mapper.WtfDspCodeTemplateMapper;
import com.wtf.dsp.admin.mapper.WtfDspDbConfigMapper;
import com.wtf.dsp.admin.mapper.WtfDspVmMapper;
import com.wtf.dsp.admin.model.PageSql;
import com.wtf.dsp.admin.model.TemplateDefaultDynamicConfig;
import com.wtf.dsp.admin.model.dto.*;
import com.wtf.dsp.admin.model.vo.ColumnVO;
import com.wtf.dsp.admin.model.vo.DsVO;
import com.wtf.dsp.admin.model.vo.PreviewCodeVO;
import com.wtf.dsp.admin.model.vo.TableVO;
import com.wtf.dsp.admin.service.DataSourceService;
import com.wtf.dsp.admin.service.DataBaseSerivce;
import com.wtf.dsp.admin.core.table.DbTableSql;
import com.wtf.dsp.admin.util.GeneratorUtil;
import com.wtf.dsp.admin.core.parser.SqlParser;
import com.wtf.dsp.core.constant.TemplateDefaultConfigConstant;
import com.wtf.dsp.core.enums.SqlResponseMapper;
import com.wtf.dsp.core.enums.TableApi;
import com.wtf.dsp.core.util.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
 * date: 2022/6/18 10:59
 * @author linhuanyou
 * @email 496558736@qq.com
 * @version 1.0
 */
@Slf4j
@Service
public class DataBaseSerivceImpl implements DataBaseSerivce {

    @Resource
    private WtfDspCodeTemplateMapper wtfDspCodeTemplateMapper;

    @Resource
    private WtfDspVmMapper wtfDspVmMapper;

    @Autowired
    private DataSourceService dynamicRoutingDataSourceService;

    @Autowired
    private AbstractMybatisConfigProvider mybatisConfigProvider;

    @Autowired
    private DbTableSql dbTableSqlSupport;

    @Autowired
    private WtfDspDbConfigMapper wtfDspDbConfigMapper;

    public static final String TABLE_NAME_FIELD = "tableName";

    @Override
    public PageUtils<TableVO> tablePage(TablePageDTO tableDTO) throws Exception {
        List<DsVO> list = dynamicRoutingDataSourceService.list(tableDTO.getNs(), tableDTO.getDs());
        if(CollUtil.isEmpty(list)){
            return new PageUtils(new ArrayList<>(), 0, tableDTO.getLimit(), tableDTO.getPage());
        }
        DsVO dsVO = list.stream().findFirst().get();
        DbType dbType = dsVO.getDbType();
        Map<String,Object> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,tableDTO.getTableName());
        String tableListSql = dbTableSqlSupport.tableSql(dbType, TableApi.TABLElIST,params);
        DsSqlDTO dsSqlDTO = new DsSqlDTO();
        dsSqlDTO.setDbType(dbType);
        dsSqlDTO.setDs(dsVO.getDs());
        dsSqlDTO.setNs(dsVO.getNs());
        dsSqlDTO.setTableApi(TableApi.TABLElIST);
        dsSqlDTO.setSqlResponseMapper(SqlResponseMapper.PAGE);
        PageSql pageSql = new PageSql();
        pageSql.setPage(tableDTO.getPage());
        pageSql.setLimit(tableDTO.getLimit() );
        pageSql.setExecSql(SqlParser.dialectSql(tableListSql,tableDTO.getPage(),tableDTO.getLimit(),dbType));
        pageSql.setTotalSql(SqlParserUtils.getOptimizeCountSql(true,null,pageSql.getExecSql()).getSql());
        dsSqlDTO.setPageSql(pageSql);
        PageUtils<TableVO> pageUtils = mybatisConfigProvider.execute(dsSqlDTO, new TypeReference<PageUtils<TableVO>>() {
        });
        pageUtils.getList().forEach(new Consumer<TableVO>() {
            @Override
            public void accept(TableVO tableVO) {
                tableVO.setDs(dsVO.getDs());
            }
        });
        return pageUtils;
    }

    @Override
    public byte[] generatorCode(CodeDTO codeDTO) throws Exception {
        TemplateDefaultDynamicConfig templateDynamicConfigVO = getTemplate(codeDTO.getNs(),codeDTO.getDs(),codeDTO.getTemplateId());
        if(StringUtils.isBlank(templateDynamicConfigVO.getTablePrefix())) {
            templateDynamicConfigVO.setTablePrefix("") ;
        }
        LambdaQueryWrapper<WtfDspVm> queryWrapper = new LambdaQueryWrapper<WtfDspVm>();
        queryWrapper.orderByAsc(WtfDspVm::getSort);
        List<GeneratorInfo> vmTemplates = wtfDspVmMapper.selectList(queryWrapper).stream().map(e->{
            GeneratorInfo generatorInfo = new GeneratorInfo();
            generatorInfo.setGeneratorFileName(e.getVmTemplate());
            generatorInfo.setGeneratorFilePath(e.getGeneratorPath());
            generatorInfo.setCommonFile(e.getCommonFile() == 1);
            return generatorInfo;
        }).collect(Collectors.toList());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        GeneratorUtil.generatorCommonCode(vmTemplates.stream().filter(e->{return e.isCommonFile();}).collect(Collectors.toList()), templateDynamicConfigVO,zip);
        for (String tableName : codeDTO.getTableNames()) {
            TablePageDTO tableDTO = new TablePageDTO();
            tableDTO.setDs(codeDTO.getDs());
            tableDTO.setNs(codeDTO.getNs());
            tableDTO.setTableName(tableName);
            //查询表信息
            TableVO table = queryTable(tableDTO);
            //查询列信息
            List<ColumnVO> columns = queryColumns(tableDTO);
            //生成代码
            GeneratorUtil.generatorTableCode(vmTemplates.stream().filter(e->{return !e.isCommonFile();}).collect(Collectors.toList()), table, columns, templateDynamicConfigVO,zip);
        }
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    @Override
    public List<ColumnVO> columns(ColumsDTO columsDTO) throws Exception {
        DsVO dataSource = dynamicRoutingDataSourceService.list(columsDTO.getNs(),columsDTO.getDs()).stream().findFirst().get();
        DbType dbType = dataSource.getDbType();
        Map<String,Object> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,columsDTO.getTableName());
        String columnsSql = dbTableSqlSupport.tableSql(dbType, TableApi.COLUMNS,params);
        DsSqlDTO dsSqlDTO = new DsSqlDTO();
        dsSqlDTO.setExcSql(columnsSql);
        dsSqlDTO.setDbType(dbType);
        dsSqlDTO.setDs(dataSource.getDs());
        dsSqlDTO.setNs(dataSource.getNs());
        dsSqlDTO.setTableApi(TableApi.COLUMNS);
        dsSqlDTO.setSqlResponseMapper(SqlResponseMapper.MAP_LIST);
        List<ColumnVO> columns = mybatisConfigProvider.execute(dsSqlDTO,new TypeReference<List<ColumnVO>>(){});
        return columns;
    }

    @Override
    public List<PreviewCodeVO> preview(CodeDTO codeDTO) throws Exception {
        List<PreviewCodeVO> dataMap = new ArrayList<>();
        TemplateDefaultDynamicConfig templateDynamicConfigVO = getTemplate(codeDTO.getNs(),codeDTO.getDs(),codeDTO.getTemplateId());
        if(StringUtils.isBlank(templateDynamicConfigVO.getTablePrefix())) {
            templateDynamicConfigVO.setTablePrefix("") ;
        };
        LambdaQueryWrapper<WtfDspVm> queryWrapper = new LambdaQueryWrapper<WtfDspVm>();
        queryWrapper.orderByAsc(WtfDspVm::getSort);
        List<GeneratorInfo> vmTemplates = wtfDspVmMapper.selectList(queryWrapper).stream().map(e->{
            GeneratorInfo generatorInfo = new GeneratorInfo();
            generatorInfo.setGeneratorFileName(e.getVmTemplate());
            generatorInfo.setGeneratorFilePath(e.getGeneratorPath());
            return generatorInfo;
        }).collect(Collectors.toList());
        for (String tableName : codeDTO.getTableNames()) {
            TablePageDTO tableDTO = new TablePageDTO();
            tableDTO.setDs(codeDTO.getDs());
            tableDTO.setNs(codeDTO.getNs());
            tableDTO.setTableName(tableName);
            //查询表信息
            TableVO table = queryTable(tableDTO);
            //查询列信息
            List<ColumnVO> columns = queryColumns(tableDTO);
            //生成代码
            dataMap.addAll(GeneratorUtil.previewCode(vmTemplates,table, columns, templateDynamicConfigVO));
        }
        return dataMap;
    }

    @Override
    public PageUtils dbConfigPage(DbConfigPageDTO configPageDTO) {
        Page<?> page = PageHelper.startPage(configPageDTO.getPage(), configPageDTO.getLimit());
        LambdaQueryWrapper<WtfDspDbConfig> wtfDspDbConfigLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if(StringUtils.isNotBlank(configPageDTO.getDb())){
            wtfDspDbConfigLambdaQueryWrapper.like(WtfDspDbConfig::getDb,configPageDTO.getDb());
        }
        List<WtfDspDbConfig> wtfDspDbConfigs = wtfDspDbConfigMapper.selectList(wtfDspDbConfigLambdaQueryWrapper);
        return new PageUtils(wtfDspDbConfigs, (int) page.getTotal(), configPageDTO.getLimit(),configPageDTO.getPage());
    }

    @Override
    public int addDbConfig(DbConfigAddDTO dbConfigAddDTO) {
        DbType dbType = dbConfigAddDTO.getDbType();
        WtfDspDbConfig wtfDspDbConfig = wtfDspDbConfigMapper.selectById(dbType.getDb());
        if(wtfDspDbConfig != null){
            throw new WtfDspException(CommonErrorEnum.DB_CONFIG_EXIST);
        }
        wtfDspDbConfig = new WtfDspDbConfig();
        wtfDspDbConfig.setDb(dbType.getDb());
        wtfDspDbConfig.setDescription(dbType.getDesc());
        wtfDspDbConfig.setJdbcUrl(dbConfigAddDTO.getJdbcUrl());
        wtfDspDbConfig.setDriverClass(dbConfigAddDTO.getDriverClass());
        if(!mybatisConfigProvider.testDriver(dbConfigAddDTO.getDriverClass())){
            throw new WtfDspException(CommonErrorEnum.DB_DRIVER_ERROR);
        }
        return wtfDspDbConfigMapper.insert(wtfDspDbConfig);
    }

    @Override
    public boolean driverClassTest(DbConfigDriverTestDTO dbConfigDriverTestDTO) {
        return mybatisConfigProvider.testDriver(dbConfigDriverTestDTO.getDriverClass());
    }

    @Override
    public List<DbType> dbConfigList() {
        return wtfDspDbConfigMapper.selectList(new QueryWrapper<WtfDspDbConfig>()).stream().map(e->{
            return DbType.getDbType(e.getDb());
        }).collect(Collectors.toList());
    }

    @Override
    public int removeDbConfig(DbConfigDelDTO dbConfigDelDTO) {
        return wtfDspDbConfigMapper.deleteBatchIds(dbConfigDelDTO.getDbs());
    }

    @Override
    public int updateDbConfig(DbConfigUpdateDTO dbConfigUpdateDTO) {
        WtfDspDbConfig wtfDspDbConfig = wtfDspDbConfigMapper.selectById(dbConfigUpdateDTO.getDb());
        if(wtfDspDbConfig == null){
            throw new WtfDspException(CommonErrorEnum.DB_CONFIG_NOT_EXIST);
        }
        if(StringUtils.isBlank(dbConfigUpdateDTO.getJdbcUrl()) && StringUtils.isBlank(dbConfigUpdateDTO.getDriverClass())){
            log.warn("Not Update");
            return 0;
        }
        if(StringUtils.isNotBlank(dbConfigUpdateDTO.getJdbcUrl()) && !wtfDspDbConfig.getJdbcUrl().equals(dbConfigUpdateDTO.getJdbcUrl())){
            wtfDspDbConfig.setJdbcUrl(dbConfigUpdateDTO.getJdbcUrl());
        }
        if(StringUtils.isNotBlank(dbConfigUpdateDTO.getDriverClass()) && !wtfDspDbConfig.getDriverClass().equals(dbConfigUpdateDTO.getDriverClass())){
            if(!mybatisConfigProvider.testDriver(dbConfigUpdateDTO.getDriverClass())){
                throw new WtfDspException(CommonErrorEnum.DB_DRIVER_ERROR);
            }
            wtfDspDbConfig.setDriverClass(dbConfigUpdateDTO.getDriverClass());
        }
        return wtfDspDbConfigMapper.updateById(wtfDspDbConfig);
    }

    private TemplateDefaultDynamicConfig getTemplate(String ns,String ds, Integer templateId) {
        if(templateId == null){
            return defaultTemplate();
        }
        LambdaQueryWrapper<WtfDspCodeTemplate> wtfTemplateConfigLambdaQueryWrapper = new LambdaQueryWrapper<>();
        wtfTemplateConfigLambdaQueryWrapper.eq(WtfDspCodeTemplate::getId,templateId);
        wtfTemplateConfigLambdaQueryWrapper.eq(WtfDspCodeTemplate::getNs,ns);
        wtfTemplateConfigLambdaQueryWrapper.eq(WtfDspCodeTemplate::getDs,ds);
        WtfDspCodeTemplate wtfDspCodeTemplate = wtfDspCodeTemplateMapper.selectOne(wtfTemplateConfigLambdaQueryWrapper);
        if(wtfDspCodeTemplate == null){
            return defaultTemplate();
        }
        TemplateDefaultDynamicConfig templateDefaultDynamicConfig = new TemplateDefaultDynamicConfig();
        BeanUtils.copyProperties(wtfDspCodeTemplate, templateDefaultDynamicConfig);
        return templateDefaultDynamicConfig;
    }

    private TemplateDefaultDynamicConfig defaultTemplate() {
        TemplateDefaultDynamicConfig templateDynamicConfigVO = new TemplateDefaultDynamicConfig();
        templateDynamicConfigVO.setAuthor(TemplateDefaultConfigConstant.AUTHOR);
        templateDynamicConfigVO.setPackageReference(TemplateDefaultConfigConstant.PACKAGE_REFERENCE);
        templateDynamicConfigVO.setModuleName(TemplateDefaultConfigConstant.MODULE_NAME);
        templateDynamicConfigVO.setEmail(TemplateDefaultConfigConstant.EMAIL);
        return templateDynamicConfigVO;
    }

    private TableVO queryTable(TablePageDTO tableDTO) throws Exception {
        DsVO dataSource = dynamicRoutingDataSourceService.list(tableDTO.getNs(),tableDTO.getDs()).stream().findFirst().get();
        DbType dbType = dataSource.getDbType();
        Map<String,Object> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,tableDTO.getTableName());
        String tableSql = dbTableSqlSupport.tableSql(dbType, TableApi.TABLE,params);
        DsSqlDTO dsSqlDTO = new DsSqlDTO();
        dsSqlDTO.setExcSql(tableSql);
        dsSqlDTO.setDbType(dbType);
        dsSqlDTO.setDs(dataSource.getDs());
        dsSqlDTO.setNs(dataSource.getNs());
        dsSqlDTO.setTableApi(TableApi.TABLE);
        dsSqlDTO.setSqlResponseMapper(SqlResponseMapper.MAP);
        TableVO table = mybatisConfigProvider.execute(dsSqlDTO,new TypeReference<TableVO>(){});
        return table;
    }

    private List<ColumnVO> queryColumns(TablePageDTO tableDTO) throws Exception {
        DsVO dataSource = dynamicRoutingDataSourceService.list(tableDTO.getNs(),tableDTO.getDs()).stream().findFirst().get();
        DbType dbType = dataSource.getDbType();
        Map<String,Object> params = new HashMap<>();
        params.put(TABLE_NAME_FIELD,tableDTO.getTableName());
        String columnsSql = dbTableSqlSupport.tableSql(dbType, TableApi.COLUMNS,params);
        DsSqlDTO dsSqlDTO = new DsSqlDTO();
        dsSqlDTO.setExcSql(columnsSql);
        dsSqlDTO.setDbType(dbType);
        dsSqlDTO.setDs(dataSource.getDs());
        dsSqlDTO.setNs(dataSource.getNs());
        dsSqlDTO.setTableApi(TableApi.COLUMNS);
        dsSqlDTO.setSqlResponseMapper(SqlResponseMapper.MAP_LIST);
        List<ColumnVO> columns = mybatisConfigProvider.execute(dsSqlDTO,new TypeReference<List<ColumnVO>>(){});
        return columns;
    }
}
